# LeetCode 518、零钱兑换II

# 一、题目描述

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10] 
输出:1

提示:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • coins 中的所有值 互不相同
  • 0 <= amount <= 5000

# 二、题目解析

# 三、参考代码

# 1、Java 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 微信:wzb_3377
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 零钱兑换II(LeetCode 518):https://leetcode.cn/problems/coin-change-2/submissions/
class Solution {
    public int change(int amount, int[] coins) {
        // 经典的完全背包问题

        // 二维数组:状态定义:dp[i][j]表示从数组 coins 中前 i 个硬币中选择得到价值为 j 的硬币组合数
        int[][] dp = new int[coins.length + 1][ amount + 1 ];

        // 只有当不选取任何硬币时,金额之和才为 0,此时只有 1 种硬币组合
        dp[0][0] = 1;

        // Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
        // coins 数组的大小就是硬币的个数
        for( int i = 1 ; i <= coins.length ; i++){
            // 遍历背包容量
            for( int j = 0 ; j <= amount ; j++){

                // 背包容量小于当前硬币的值,即背包容量已经不足以拿第 i 个硬币了
                // 第 i 个硬币即下标为 i - 1 的那个硬币
                if( j < coins[i - 1]){

                    // 硬币组合数就是去考虑当前背包容量情况下,从前 i - 1 个物品中选择出的硬币组合数
                    dp[i][j] = dp[i - 1][j];

                // 背包容量足够拿第 i 个物品,那么可拿也可不拿
                // 1、拿了,那么硬币组合数是前 i 个硬币扣除第 i 个硬币的情况下的硬币组合数
                // 2、不拿,那么就是从前 i - 1 个硬币中选择出的硬币组合数
                }else{

                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
                }

                // System.out.print(dp[i][j] + "");
            }
        //  System.out.println();
        }

        return dp[coins.length][amount];

    }
}

# 一维****DP

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 微信:wzb_3377
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 零钱兑换II(LeetCode 518):https://leetcode.cn/problems/coin-change-2/submissions/
class Solution {
    public int change(int amount, int[] coins) {
        // 经典的完全背包问题

        // 二维数组:状态定义:dp[i][j]表示从数组 coins 中前 i 个硬币中选择得到价值为 j 的硬币组合数
        int[] dp = new int[ amount + 1 ];

        // 只有当不选取任何硬币时,金额之和才为 0,此时只有 1 种硬币组合
        dp[0] = 1;

        // Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
        // coins 数组的大小就是硬币的个数
        for( int i = 1 ; i <= coins.length ; i++){
            // 遍历背包容量
            for( int j = 0 ; j <= amount ; j++){

                // 背包容量足够拿第 i 个物品,那么可拿也可不拿
                // 1、拿了,那么硬币组合数是前 i 个硬币扣除第 i 个硬币的情况下的硬币组合数
                // 2、不拿,那么就是从前 i - 1 个硬币中选择出的硬币组合数
                if( j >= coins[i - 1]){
                    // 硬币组合数就是去考虑当前背包容量情况下,从前 i - 1 个物品中选择出的硬币组合数
                    dp[j] = dp[j] + dp[j-coins[i-1]];

                }
            }
        }

        return dp[amount];

    }
}

# **2、**C++ 代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {

        // 经典的完全背包问题

        // 二维数组:状态定义:dp[i][j]表示从数组 coins 中前 i 个硬币中选择得到价值为 j 的硬币组合数
        int n = coins.size();
        vector<vector<int>>dp(n + 1, vector<int>(amount + 1 , 0));

        // 只有当不选取任何硬币时,金额之和才为 0,此时只有 1 种硬币组合
        dp[0][0] = 1;

        // Tips:前 i 个物品表示的是编号从 [ 0 , i - 1] 这些物品,即最后一个物品编号为 i - 1
        // coins 数组的大小就是硬币的个数
        for( int i = 1 ; i <= n ; i++){
            // 遍历背包容量
            for( int j = 0 ; j <= amount ; j++){

                // 背包容量小于当前硬币的值,即背包容量已经不足以拿第 i 个硬币了
                // 第 i 个硬币即下标为 i - 1 的那个硬币
                if( j < coins[i - 1]){

                    // 硬币组合数就是去考虑当前背包容量情况下,从前 i - 1 个物品中选择出的硬币组合数
                    dp[i][j] = dp[i - 1][j];

                // 背包容量足够拿第 i 个物品,那么可拿也可不拿
                // 1、拿了,那么硬币组合数是前 i 个硬币扣除第 i 个硬币的情况下的硬币组合数
                // 2、不拿,那么就是从前 i - 1 个硬币中选择出的硬币组合数
                }else{

                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
                }

            }

        }

        return dp[n][amount];

    }
};

# 3、Python 代码

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        
        n = len(coins)
        dp = [[0]*(amount+1) for _ in range(n+1)] 

        # 初始化
        dp[0][0] = 1    
        
        # 完全背包
        # 第一层循环:遍历硬币
        for i in range(1, n+1):  

            # 第二层循环:遍历背包
            for j in range(amount+1):   
                # 容量有限,无法选择第i个硬币
                if j < coins[i-1]:     
                    dp[i][j] = dp[i-1][j]
                # 可选择第i个硬币
                else:                  
                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]]
        
        return dp[n][amount]